package org.dromara.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.dromara.common.core.exception.BusinessException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.config.core.RedisDelayMQSender;
import org.dromara.system.domain.SysMemberGroup;
import org.dromara.system.domain.SysUserMachine;
import org.dromara.system.domain.SysUserMachinePolicy;
import org.dromara.system.domain.bo.QueryTradingBo;
import org.dromara.system.domain.bo.StageActTaskBo;
import org.dromara.system.domain.bo.SysUserMachinePolicyBo;
import org.dromara.system.domain.bo.SysUserPurseBo;
import org.dromara.system.domain.vo.*;
import org.dromara.system.enums.MachineConstant;
import org.dromara.system.enums.RedisMQConstant;
import org.dromara.system.enums.SysUserPointsRecordsEnum;
import org.dromara.system.mapper.SysUserMachinePolicyMapper;
import org.dromara.system.mapper.TradingRecordsMapper;
import org.dromara.system.service.*;
import org.dromara.system.service.impl.permission.PermissionBaseService;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
 * 机具政策Service业务层处理
 *
 * @author Lion Li
 * @date 2025-07-10
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class SysUserMachinePolicyServiceImpl extends ServiceImpl<SysUserMachinePolicyMapper, SysUserMachinePolicy>
    implements ISysUserMachinePolicyService {

    private final ISysUserMachineService sysUserMachineService;


    private final TradingRecordsMapper tradingRecordsMapper;

    private final ISysUserPurseService sysUserPurseService;

    private final ISysUserFreezePointsRecordsService sysUserFreezePointsRecordsService;

    private final ISysUserPointsRecordsService sysUserPointsRecordsService;

    private final ISysUserService sysUserService;

    private final ISysUserMemberConfigService sysUserMemberConfigService;

    private final ISysMemberGroupService sysMemberGroupService;
    private final PermissionBaseService permissionBaseService;

    @Resource
    private RedisDelayMQSender redisDelayMQSender;

    /**
     * 查询机具政策
     *
     * @param id 主键
     * @return 机具政策
     */
    @Override
    public SysUserMachinePolicyVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询机具政策列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 机具政策分页列表
     */
    @Override
    public TableDataInfo<SysUserMachinePolicyVo> queryPageList(SysUserMachinePolicyBo bo, PageQuery pageQuery) {
        permissionBaseService.aloneAssignPermission(bo);
        IPage<SysUserMachinePolicyVo> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
        IPage<SysUserMachinePolicyVo> list = baseMapper.findAll(page, bo);
        return TableDataInfo.build(list);
    }

    /**
     * 查询符合条件的机具政策列表
     *
     * @param bo 查询条件
     * @return 机具政策列表
     */
    @Override
    public List<SysUserMachinePolicyVo> queryList(SysUserMachinePolicyBo bo) {
        LambdaQueryWrapper<SysUserMachinePolicy> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<SysUserMachinePolicy> buildQueryWrapper(SysUserMachinePolicyBo bo) {
        LambdaQueryWrapper<SysUserMachinePolicy> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(SysUserMachinePolicy::getId);
        lqw.eq(bo.getUserId() != null, SysUserMachinePolicy::getUserId, bo.getUserId());
        lqw.like(StringUtils.isNotBlank(bo.getPolicyName()), SysUserMachinePolicy::getPolicyName, bo.getPolicyName());
        lqw.eq(bo.getStageCheckMonth() != null, SysUserMachinePolicy::getStageCheckMonth, bo.getStageCheckMonth());
        lqw.eq(bo.getStageCheckAmountMonth() != null, SysUserMachinePolicy::getStageCheckAmountMonth, bo.getStageCheckAmountMonth());
        lqw.eq(bo.getStageCashBackAmount() != null, SysUserMachinePolicy::getStageCashBackAmount, bo.getStageCashBackAmount());
        lqw.eq(bo.getCompleteCheckDay() != null, SysUserMachinePolicy::getCompleteCheckDay, bo.getCompleteCheckDay());
        lqw.eq(bo.getCompleteCashBackAmount() != null, SysUserMachinePolicy::getCompleteCashBackAmount, bo.getCompleteCashBackAmount());
        return lqw;
    }

    /**
     * 新增机具政策
     *
     * @param bo 机具政策
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(SysUserMachinePolicyBo bo) {
        if (ObjectUtil.isNull(bo.getUserId())) {
            bo.setUserId(LoginHelper.getUserId());
        }
        SysUserMachinePolicy add = MapstructUtils.convert(bo, SysUserMachinePolicy.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改机具政策
     *
     * @param bo 机具政策
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(SysUserMachinePolicyBo bo) {
        SysUserMachinePolicy update = MapstructUtils.convert(bo, SysUserMachinePolicy.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(SysUserMachinePolicy entity) {
        //TODO 做一些数据校验,如唯一约束
        Integer stageBackBindDeposit = entity.getStageBackBindDeposit();
        Integer stageBackBindMember = entity.getStageBackBindMember();
        if (ObjectUtil.equal(stageBackBindDeposit, MachineConstant.NUMBER.ONE)
            && ObjectUtil.equal(stageBackBindMember, MachineConstant.NUMBER.ONE)) {
            throw new BusinessException("绑定押金和绑定会员不能同时选中！");

        }
    }

    /**
     * 校验并批量删除机具政策信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
            List<SysUserMachine> list = sysUserMachineService.lambdaQuery().in(SysUserMachine::getMachinePolicyId, ids).list();
            BusinessException.isTrue(CollUtil.isNotEmpty(list), "政策下有机器禁止删除！");
        }
        return baseMapper.deleteByIds(ids) > 0;
    }


    @Override
    public StageActTaskVo stageActTask(StageActTaskBo bo) {
        //查询机器信息
        String sn = bo.getSn();
        SysUserMachine sysUserMachine = sysUserMachineService.lambdaQuery().eq(SysUserMachine::getSn, sn).one();
        BusinessException.isNull(sysUserMachine, "查无此机器！");
        Long userId = sysUserMachine.getBindUserId();
        SysUserVo sysUserVo = sysUserService.selectUserById(userId);
        Long memberGroupId = sysUserVo.getMemberGroupId();
        SysMemberGroup sysMemberGroup = sysMemberGroupService.lambdaQuery().eq(SysMemberGroup::getId, memberGroupId).one();
        BigDecimal complianceCheckAmount = sysMemberGroup.getComplianceCheckAmount();
        Integer complianceCheckMonth = sysMemberGroup.getComplianceCheckMonth();
        BigDecimal complianceRewards = sysMemberGroup.getComplianceRewards();

        if (complianceCheckMonth <= MachineConstant.NUMBER.ZERO) {
            return StageActTaskVo.builder()
                .returnCash(BigDecimal.ZERO)
                .isFinishCount(MachineConstant.NUMBER.ZERO)
                .arrayList(Lists.newArrayList())
                .sn(sysUserMachine.getSn())
                .flag(MachineConstant.NUMBER.ZERO).build();
        }
        //获取周期 从次月开始
        ArrayList<StageActTaskDetailVo> queryCardSwipingTaskList = queryCardSwipingTaskList(sysUserMachine, sysMemberGroup);
        //0已失效  1进行中  2待领取 3已领取
        long isFinishCount = queryCardSwipingTaskList.stream().filter(t -> ObjectUtil.equal(t.getIsFinish(), 1)).count();

        //查询该sn是否已经领取过商户阶段返现，如果已经领取则直接返回
        return StageActTaskVo.builder()
            .arrayList(queryCardSwipingTaskList)
            .finishMonth(complianceCheckMonth)
            .returnCash(complianceRewards)
            .isFinishCount(Convert.toInt(isFinishCount))
            .flag(sysUserMachine.getIsStageBack())
            .sn(sysUserMachine.getSn())
            .build();
    }


    public ArrayList<StageActTaskDetailVo> queryCardSwipingTaskList(SysUserMachine
                                                                        sysUserMachine, SysMemberGroup sysMemberGroup) {
        Integer stageCheckMonth = sysMemberGroup.getComplianceCheckMonth();
        BigDecimal stageCheckAmountMonth = sysMemberGroup.getComplianceCheckAmount();
        Date createTime = sysUserMachine.getBindTime();
        ArrayList<StageActTaskDetailVo> queryCardSwipingTaskList = new ArrayList<>();
        ArrayList<String> arrayList = Lists.newArrayList();
        for (int i = 1; i <= stageCheckMonth; i++) {
            String format = new SimpleDateFormat("yyyy-MM").format(DateUtil.offsetMonth(createTime, i));
            arrayList.add(format);
        }
        for (String dealTime : arrayList) {
            String[] split = dealTime.split("-");
            LocalDateTime localDateTime = LocalDateTime.of(Convert.toInt(split[0]), Convert.toInt(split[1]), 1, 1, 1);
            LocalDateTime startOfMonth = localDateTime.with(TemporalAdjusters.firstDayOfMonth()).withHour(0).withMinute(0).withSecond(0).withNano(0);
            LocalDateTime endOfMonth = localDateTime.with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59).withNano(999999999);
            BigDecimal tradingAmount = getTradingAmount(sysUserMachine.getSn(), startOfMonth, endOfMonth);
            tradingAmount = NumberUtil.round(tradingAmount, 2, RoundingMode.DOWN);
            String speedProgress = Convert.toStr(tradingAmount) + "/" + Convert.toStr(stageCheckAmountMonth);
            Integer status = NumberUtil.isGreaterOrEqual(tradingAmount, stageCheckAmountMonth) ? 1 : 0;
            StageActTaskDetailVo build = StageActTaskDetailVo.builder()
                .month(dealTime)
                .finishMoney(speedProgress)
                .isFinish(status)
                .build();
            queryCardSwipingTaskList.add(build);
        }
        return queryCardSwipingTaskList;
    }


    public BigDecimal getTradingAmount(String sn, LocalDateTime startTime, LocalDateTime endTime) {
        QueryTradingBo bo = QueryTradingBo.builder()
            .sn(sn)
            .startTime(LocalDateTimeUtil.beginOfDay(startTime))
            .endTime(LocalDateTimeUtil.endOfDay(endTime)).build();
        return tradingRecordsMapper.queryTotalTransactionAmount(bo);
    }

    @Override
    public void machineCompleteCashBack() {
        log.info("执行定时达标返现：{}", LocalDate.now());
        //查询进两天有过交易的sn
        List<String> amountByMachines = tradingRecordsMapper.findAmountByMachines();
        if (CollUtil.isNotEmpty(amountByMachines)) {
            //发送消息队列执行返现
            amountByMachines.forEach(t -> {
                redisDelayMQSender.send(RedisMQConstant.COMPLETE_CASH_BACK_QUEUE, t, null, t);
            });
        }
    }

    @Override
    public void dealCompleteCashBack(String sn) {
        log.info("{}开始执行达标返现！", sn);
        SysUserMachine sysUserMachine = sysUserMachineService.lambdaQuery().eq(SysUserMachine::getSn, sn).one();
        if (ObjectUtil.equal(sysUserMachine.getIsCompleteBack(), MachineConstant.NUMBER.ONE)) {
            return;
        }
        if (ObjectUtil.isNull(sysUserMachine.getBindUserId())) {
            return;
        }
        SysUserMachinePolicy sysUserMachinePolicy = lambdaQuery().eq(SysUserMachinePolicy::getId, sysUserMachine.getMachinePolicyId()).one();
        if (ObjectUtil.equal(sysUserMachinePolicy.getCompleteCheckDay(), MachineConstant.NUMBER.ZERO)) {
            return;
        }
        BigDecimal cashBackAmount = sysUserMachinePolicy.getCompleteCashBackAmount();
        Date bindTime = sysUserMachine.getBindTime();
        Integer completeCheckDay = sysUserMachinePolicy.getCompleteCheckDay();
        DateTime checkEndTime = DateUtil.offsetDay(bindTime, completeCheckDay);
        BigDecimal tradingAmount = getTradingAmount(sn,
            bindTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(),
            checkEndTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
        if (NumberUtil.isGreaterOrEqual(tradingAmount, sysUserMachinePolicy.getCompleteCheckAmount())) {
            //如果交易金额大于考核金额则返现
            SysUserPurseBo sysUserPurseBo = SysUserPurseBo.builder()
                .userId(sysUserMachine.getBindUserId())
                .type(SysUserPointsRecordsEnum.COMPLETE_CASH_BACK_POINTS.getCode())
                .points(cashBackAmount)
                .remark(sysUserMachine.getSn())
                .build();
            sysUserPointsRecordsService.updateUserPoints(CollUtil.newArrayList(sysUserPurseBo));
            //将达标返现改为已经返现
            sysUserMachineService.lambdaUpdate()
                .set(SysUserMachine::getIsCompleteBack, MachineConstant.NUMBER.ONE)
                .eq(SysUserMachine::getSn, sn)
                .update();
        }
    }

    @Override
    public void machineStageCashBack() {
        log.info("执行定时阶段返现：{}", LocalDate.now());
        //查询设置了阶段返现的政策
        List<SysUserMachinePolicy> sysUserMachinePolicies = lambdaQuery().gt(SysUserMachinePolicy::getStageCheckMonth, MachineConstant.NUMBER.ZERO).list();
        //查询这些政策绑定的机器
        if (CollUtil.isNotEmpty(sysUserMachinePolicies)) {
            List<Long> machinePolicylist = sysUserMachinePolicies.stream().map(SysUserMachinePolicy::getId).toList();
            List<String> stageMachine = tradingRecordsMapper.findStageMachine(machinePolicylist);
            //发送消息队列执行返现
            stageMachine.forEach(t -> {
                redisDelayMQSender.send(RedisMQConstant.STAGE_CASH_BACK_QUEUE, t, null, t);
            });
        }
    }

    @Override
    public void dealStageCashBack(String sn) {
        log.info("{}开始执行阶段返现！", sn);
        SysUserMachine sysUserMachine = sysUserMachineService.lambdaQuery().eq(SysUserMachine::getSn, sn).one();
        if (ObjectUtil.equal(sysUserMachine.getIsStageBack(), MachineConstant.NUMBER.ONE)) {
            return;
        }
        if (ObjectUtil.isNull(sysUserMachine.getBindUserId())) {
            return;
        }
        SysUserMachinePolicy sysUserMachinePolicy = lambdaQuery().eq(SysUserMachinePolicy::getId, sysUserMachine.getMachinePolicyId()).one();
        Integer stageCheckMonth = sysUserMachinePolicy.getStageCheckMonth();
        if (stageCheckMonth <= MachineConstant.NUMBER.ZERO) {
            return;
        }
        BigDecimal cashBackAmount = getStageCashBackAmount(sysUserMachinePolicy, sysUserMachine);
        Date bindTime = sysUserMachine.getBindTime();
        //符合条件的开始执行时间    //次月开始计算
        ArrayList<BigDecimal> arrayList = new ArrayList<>();
        for (int j = 1; j <= sysUserMachinePolicy.getStageCheckMonth(); j++) {
            DateTime dateTime = DateUtil.offsetMonth(bindTime, j);
            DateTime beginOfMonth = DateUtil.beginOfMonth(dateTime);
            DateTime endOfMonth = DateUtil.endOfMonth(dateTime);
            BigDecimal tradingAmount = getTradingAmount(sn,
                beginOfMonth.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(),
                endOfMonth.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
            arrayList.add(tradingAmount);
        }
        boolean allMatch = arrayList.stream().allMatch(t -> NumberUtil.isGreaterOrEqual(t, sysUserMachinePolicy.getStageCheckAmountMonth()));
        if (allMatch) {
            //达标  给商户返积分
            SysUserPurseBo sysUserPurseBo = SysUserPurseBo.builder()
                .userId(sysUserMachine.getBindUserId())
                .type(SysUserPointsRecordsEnum.STAGE_CASH_BACK_POINTS.getCode())
                .points(cashBackAmount)
                .remark(sysUserMachine.getSn())
                .build();
            sysUserPointsRecordsService.updateUserPoints(CollUtil.newArrayList(sysUserPurseBo));
            //将达标返现改为已经返现
            sysUserMachineService.lambdaUpdate()
                .set(SysUserMachine::getIsStageBack, MachineConstant.NUMBER.ONE)
                .eq(SysUserMachine::getSn, sn)
                .update();
        }
    }

    public BigDecimal getStageCashBackAmount(SysUserMachinePolicy sysUserMachinePolicy, SysUserMachine sysUserMachine) {
        BigDecimal cashBackAmount = BigDecimal.ZERO;
        if (ObjectUtil.equal(sysUserMachinePolicy.getStageBackBindDeposit(), MachineConstant.NUMBER.ONE)) {
            cashBackAmount = Convert.toBigDecimal(sysUserMachine.getDeposit(), BigDecimal.ZERO);
        } else if (ObjectUtil.equal(sysUserMachinePolicy.getStageBackBindMember(), MachineConstant.NUMBER.ONE)) {
            Long bindUserId = sysUserMachine.getBindUserId();
            SysUserVo sysUserVo = sysUserService.selectUserById(bindUserId);
            Long memberTag = sysUserVo.getMemberTag();
            if (memberTag > 0) {
                SysUserMemberConfigVo sysUserMemberConfigVo = sysUserMemberConfigService.queryById(memberTag);
                cashBackAmount = Convert.toBigDecimal(sysUserMemberConfigVo.getMemberPrice(), BigDecimal.ZERO);
            }
        } else {
            cashBackAmount = Convert.toBigDecimal(sysUserMachinePolicy.getStageCashBackAmount(), BigDecimal.ZERO);
        }
        return cashBackAmount;
    }

}
